<?php
/**
 * Created by PhpStorm.
 * User: wangjiaming
 * Date: 2020/12/21
 * Time: 14:20
 */

require './vendor/autoload.php';
use Elasticsearch\ClientBuilder;

class ElasticsearchFulltextTool
{
	protected $dbName;
	protected $tbName;
	protected $client;

	public function __construct($host,$port,$isSSL = false) {
		try{
			$hosts = [
				$isSSL?'https':'http'.'://'.$host.':'.$port
			];
			$this->client = ClientBuilder::create()->setHosts($hosts)->build();
		}catch (Exception $e){
			$r = $e->getMessage();
			return json_encode($r);
		}
	}

	/**
	 * 设置数据库和表
	 * @param $dbName
	 * @param $tbName
	 */
	public function setParams($dbName,$tbName){
		$this->dbName = $dbName;
		$this->tbName = $tbName;
	}


	/**
	 * 创建索引
	 * 在设置mapping之前需要创建索引
	 */
	public function createIndex($params) {
		try{
			$r = $this->client->indices()->create($params);
		}catch (Exception $e){
			$r = $e->getMessage();
		}

		return $r;
	}

	/**
	 * 删除索引
	 * 在设置mapping之前需要创建索引
	 */
	public function deleteIndex($params) {
		try{
			$r = $this->client->indices()->delete($params);
		}catch (Exception $e){
			$r = $e->getMessage();
		}

		return $r;
	}

	/**
	 * 设置全文检索mapping
	 * @param $data
	 */
	public function setMapping($data) {
		try{
			$params = [
				'index'=>$this->dbName,
				'type'=>$this->tbName
			];
			$mapParam = [];
			foreach ($data as $field => $field_type) {
				if($field_type == 'text'){ ## 需要做全文检索的ik中文分词配置
					$mapParam[ $field ] = [
						'type'              =>  $field_type,
						'analyzer'          =>  'ik_max_word',
						'search_analyzer'   =>  'ik_max_word'
					];
				}elseif ($field_type == 'integer' || $field_type == 'long'){ ## 数值型的字段，就不需要配置ik中文分词的全文检索了！
					$mapParam[ $field ] = [
						'type' => $field_type
					];
				}else{
					$mapParam[ $field ] = [
						'type' =>  $field_type
					];
				}
			 }
			$params[ 'body' ][ $params[ 'type' ] ][ 'properties' ] = $mapParam;
			 $r = $this->client->indices()->putMapping($params);
		}catch (Exception $e){
			$r = $e->getMessage();
		}

		return $r;
	}


	/**
	 * 添加数据
	 * @param $data
	 */
	public function add($data) {
		try{
			$params = [
				'index'=>$this->dbName,
				'type'=>$this->tbName
			];
			if(array_key_exists('id', $data)){ ## 我把数据里的id取出来，做es的数据的唯一标识了，我觉得保持一致方便删除数据。
				$params['id'] = $data['id'];
			}
			$params['body'] = $data;
			$r = $this->client->index($params);
		}catch (Exception $e){
			$r = $e->getMessage();
		}

		return $r;
	}


	/**
	 * 单个字段的条件查询（简单查询，不带分页&排序）
	 * @param $data
	 */
	public function search($data) {
		try{
			$field = key($data);
			$params = [
				'index'=>$this->dbName,
				'type'=>$this->tbName
			];
			$query = [
				'match'=>[
					$field=>[
						'query'=>$data[$field],
						'minimum_should_match'=>'90%'
					]
				]
			];
			// 高亮
			$highlight = [
				'fields'=> [
					$field=>[
						'pre_tags'=>'<strong style="color:red">',
						'post_tags'=>'</strong>'
					]
				]
			];
			$params['body']['query'] = $query;
			$params['body']['highlight'] = $highlight;
			$r = $this->client->search($params);
		}catch (Exception $e){
			$r = $e->getMessage();
		}

		return $r;
	}


	/**
	 * 复杂查询
	 */
	public function searchMulit($data) {
		try{
			$params = [
				'index'=>$this->dbName,
				'type'=>$this->tbName
			];
			// 分页
			if(array_key_exists('es_size', $data) && array_key_exists('es_from', $data)){
				$params['size'] = $data['es_size'];
				$params['from'] = $data['es_from'];
				unset($data['es_size']);
				unset($data['es_from']);
			}
			// 排序：如果使用的是best_fields，不太适合使用按照时间排序，会影响相关度靠前的排序！
			if(array_key_exists('es_sort', $data)){ ## 一开始，怎么都会报错！后来，只要给需要排序的字段设置正确的数据类型就不会在报错！我这里的排序的字段是add_time设置成long类型！
				$params['body']['sort'] = [
					$data['es_sort'] =>  'desc'
				];
				unset($data['es_sort']);
			}
			// 条件组合
			if (array_key_exists('condition', $data)){
				$query = [];
				$condition = $data['condition'];
				if(array_key_exists('fulltext', $condition)){
					foreach ($condition['fulltext'] as $fields=>$keyword){
						$field_arr = explode(',',$fields);
						$query['bool']['must'] =  [
							'multi_match'=>[
								'query' =>  $keyword,
								'type' => 'best_fields', ## 希望完全匹配的结果靠前
								//'type'          =>  'phrase',               ## 完全匹配
								'tie_breaker' =>  0.3, ## 除了完全匹配的结果，分词的匹配结果靠后
								'fields' =>  $field_arr,
								//'slop'          =>  10 ## slop指定每个相邻词之间允许相隔多远。如果此处设置为0，则表示实现完全匹配！与type='phrase'配合使用，很实用！
								]
						];
						// 高亮
						$highlight = [];
						foreach ($field_arr as $field){
							$highlight['fields'][$field] = [
								'pre_tags'  =>  '<strong style="color:red">',
								'post_tags' =>  '</strong>'
							];
						}
						$params['body']['highlight'] = $highlight;
					}
				}
				if (array_key_exists('gt', $condition)){
					foreach ($condition['gt'] as $field=>$value){
						$query['bool']['filter'] =  [
							'term'=>[
								$field  => $value
							]
						];
					}
				}
			}else{
				return json_encode(array('code'=>'0','msg'=>'condition不能为空'));
			}
			$params['body']['query'] = $query;
			$r = $this->client->search($params);
		}catch (Exception $e){
			$r = $e->getMessage();
		}

		return $r;
	}


	/**
	 * 根据唯一标识id删除数据
	 * @param $data
	 */
	public function delete($id) {
		try{
			$params = [
				'index'=>$this->dbName,
				'type'=>$this->tbName
			];
			$params['id'] = $id;
			$r = $this->client->delete($params);
		}catch (Exception $e){
			$r = $e->getMessage();
		}

		return json_encode($r);
	}








}